/*
 * Copyright 2011 OpenWAF.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.openwaf.client.ui.widget;

import com.openwaf.client.dom.DivElement;
import com.openwaf.client.dom.Element;
import com.openwaf.client.dom.OListElement;
import com.openwaf.client.dom.SpanElement;
import com.openwaf.client.event.HandlerRegistration;
import com.openwaf.client.event.dom.BlurHandler;
import com.openwaf.client.event.dom.ChangeHandler;
import com.openwaf.client.event.dom.ClickEvent;
import com.openwaf.client.event.dom.ClickHandler;
import com.openwaf.client.event.dom.ContextMenuHandler;
import com.openwaf.client.event.dom.DoubleClickHandler;
import com.openwaf.client.event.dom.KeyDownHandler;
import com.openwaf.client.event.dom.KeyPressHandler;
import com.openwaf.client.event.dom.KeyUpHandler;
import com.openwaf.client.event.dom.MouseDownHandler;
import com.openwaf.client.event.dom.MouseMoveHandler;
import com.openwaf.client.event.dom.MouseOutHandler;
import com.openwaf.client.event.dom.MouseOverHandler;
import com.openwaf.client.event.dom.MouseUpHandler;
import com.openwaf.client.ui.ElementNotFoundException;
import com.openwaf.client.ui.UIControl;
import com.openwaf.client.ui.annotation.ViewElement;
import com.openwaf.client.ui.annotation.ViewMethod;
import com.openwaf.client.ui.annotation.ViewProperty;
import java.util.ArrayList;
import java.util.Iterator;

public class TreeNode extends UIControl implements Iterable<TreeNode> {

    @ViewElement
    private DivElement divImg;
    @ViewElement
    private DivElement divNode;
    @ViewElement
    private OListElement olChilds;
    @ViewElement
    private DivElement divText;
    private ArrayList<TreeNode> childs;
    private boolean expanded;
    private boolean expandHandlerAdded;
    private TreeView treeview;

    public TreeNode() {
    }

    void updateCss(){
        updateIcon();
        divNode.setCssClassName(treeview.getCssNode());
        divText.setCssClassName(treeview.getCssText());
        olChilds.setCssClassName(treeview.getCssChildBlock());
        if(childs==null) return;
        for(TreeNode n:childs){
            n.updateCss();
        }
    }
    public TreeNode(String text) {
        setText(text);
    }
    void setTreeView(TreeView tv){
        treeview=tv;
        if(childs==null) return;
        for(TreeNode tn:childs){
            tn.setTreeView(tv);
        }
    }
    private TreeView getTreeView(){
        return treeview;
    }

    private void beforeAddChild() {
        //just to optimize
        if (expandHandlerAdded == false) {
            childs = new ArrayList<TreeNode>();
            divImg.addClickHandler(new ClickHandler() {

                public void onClick(ClickEvent event) {
                    if (expanded) {
                        collapse();
                    } else {
                        expand();
                    }
                }
            });
            expandHandlerAdded = true;
        }
    }

    public boolean hasChild() {
        if (childs == null) {
            return false;
        }
        return !childs.isEmpty();
    }

    @ViewMethod
    public void addChild(TreeNode tn) {
        if (childs == null) {
            beforeAddChild();
        }
        tn.setTreeView(treeview);
        childs.add(tn);
        olChilds.appendChild(tn.getElement());
        updateIcon();
    }

    public void addChild(int index, TreeNode tn) {
        if (childs == null) {
            beforeAddChild();
        }
        if (index < 0 || index > childs.size()) {
            throw new IndexOutOfBoundsException("Index out of range in TreeNode::addChildAt");
        }
        tn.setTreeView(treeview);
        childs.add(tn);
        olChilds.appendChild(tn.getElement());
        updateIcon();
    }

    public TreeNode getChild(int index) {
        if (index < 0 || index > childs.size()) {
            throw new IndexOutOfBoundsException("Index out of range in TreeNode::addChildAt");
        }
        return childs.get(index);
    }

    public void removeChild(TreeNode tn) {
        int index = childs.indexOf(tn);
        if (index < 0) {
            throw new ElementNotFoundException("Element not found in child list");
        }
        removeChild(index);
    }

    public void removeChild(int index) {
        if (index < 0 || index > childs.size()) {
            throw new IndexOutOfBoundsException("Index out of range in TreeNode::addChildAt");
        }
        TreeNode tn = childs.get(index);
        tn.setTreeView(null);
        olChilds.removeChild(tn.getElement());
        updateIcon();
    }

    private void updateIcon() {
        if(treeview==null) return;
        if (hasChild()) {
            if (expanded) {
                divImg.setCssClassName(treeview.getCssNodeStateExpanded());
            } else {
                divImg.setCssClassName(treeview.getCssNodeStateCollapsed());
            }
        } else {
            divImg.setCssClassName(treeview.getCssNodeStateLeaf());
        }
    }

    @ViewProperty
    public void setText(String text) {
        divText.setInnerHTML(text);
    }

    @ViewMethod
    public void setText(Element element) {
        divText.clearChilds();
        divText.appendChild(element);
    }

    public String getText() {
        return divText.getInnerHTML();
    }

    public boolean isExpanded() {
        return expanded;
    }

    @ViewProperty
    public void expand() {
        if (!expanded) {
            if(treeview!=null){
                divImg.setCssClassName(treeview.getCssNodeStateExpanded());
            }
            olChilds.getStyle().setDisplay("");
            expanded = true;
        }
    }

    @ViewProperty
    public void collapse() {
        if (expanded) {
            if(treeview!=null){
                divImg.setCssClassName(treeview.getCssNodeStateCollapsed());
            }
            olChilds.getStyle().setDisplay("none");
            expanded = false;
        }
    }

    public Iterator<TreeNode> iterator() {
        if (childs == null) {
            childs = new ArrayList<TreeNode>();
        }
        return childs.iterator();
    }

    @Override
    public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
        return divText.addKeyDownHandler(handler);
    }

    @Override
    public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
        return divText.addKeyUpHandler(handler);
    }

    @Override
    public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
        return divText.addKeyPressHandler(handler);
    }

    @Override
    public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
        return divText.addMouseDownHandler(handler);
    }

    @Override
    public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
        return divText.addMouseMoveHandler(handler);
    }

    @Override
    public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
        return divText.addMouseOutHandler(handler);
    }

    @Override
    public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
        return divText.addMouseOverHandler(handler);
    }

    @Override
    public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
        return divText.addMouseUpHandler(handler);
    }

    @Override
    public HandlerRegistration addClickHandler(ClickHandler handler) {
        return divText.addClickHandler(handler);
    }

    @Override
    public HandlerRegistration addContextMenuHandler(ContextMenuHandler handler) {
        return divText.addContextMenuHandler(handler);
    }

    @Override
    public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
        return divText.addDoubleClickHandler(handler);
    }
}
